package com.demo.common.mq;

import com.demo.common.exceptions.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 消息处理器
 */
@Component
@Slf4j
public class MessageHandler implements BeanPostProcessor {
    /**
     * 队列和消费方法缓存
     */
    private Map<String, MessageHandleMethod> handleMethodMap = new HashMap<>();

    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> userClass = ClassUtils.getUserClass(bean);
        //找到所有带MQListener注解的方法
        List<Method> methodsListWithAnnotation = MethodUtils.getMethodsListWithAnnotation(userClass, MQListener.class);
        if (!CollectionUtils.isEmpty(methodsListWithAnnotation)) {
            for (Method method : methodsListWithAnnotation) {
                //消费方法必须是method(String)的形式，单参数，参数类型String
                if (method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(String.class)) {
                    MQListener mqListener = method.getAnnotation(MQListener.class);
                    MessageHandleMethod oldMethod = handleMethodMap.get(mqListener.queue());
                    //同队列有不同的消费方法，报错
                    if (oldMethod != null) {
                        String oldMethodPath = oldMethod.getMethod().getDeclaringClass().getName() + "#" + oldMethod.getMethod().getName();
                        String currentMethodPath = userClass.getName() + "#" + method.getName();
                        String errorMsg = MessageFormat.format("MQ监听队列{0}处理方法重复,重复方法是{1}和{2}", mqListener.queue(), oldMethodPath, currentMethodPath);
//                        String errorMsg = "MQ监听队列" + mqListener.queue() + "处理方法重复, 重复方法是" + oldMethodPath + "和" + currentMethodPath;
//                        String errorMsg = String.format("MQ监听队列%s处理方法重复,重复方法是%s和%s", mqListener.queue(), oldMethodPath, currentMethodPath);

                        throw new ServiceException(errorMsg);
                    }
                    MessageHandleMethod messageHandleMethod = new MessageHandleMethod(method, bean);
                    handleMethodMap.put(mqListener.queue(), messageHandleMethod);
                } else {
                    log.warn("MQ监听处理方法校验失败，方法必须是单参数且参数类型为String的方法，校验失败的方法是{}#{}", userClass.getName(), method.getName());
                }
            }
        }
        return bean;
    }


    /**
     * 是否支持处理该队列的消息
     *
     * @param queue
     * @return
     */
    public boolean support(String queue) {
        return handleMethodMap.containsKey(queue);
    }

    /**
     * 消息处理
     *
     * @param queue
     * @param msg
     */
    public boolean handleMsg(String queue, String msg) {
        MessageHandleMethod handleMethod = handleMethodMap.get(queue);
        if (handleMethod != null) {
            try {
                //反射处理message
                handleMethod.getMethod().invoke(handleMethod.getTargetBean(), msg);
                log.info("处理队列{}消息{}成功", queue, msg);
                return true;
            } catch (Exception e) {
                log.error("处理队列{}消息{}失败", queue, msg, e);
                return false;
            }
        } else {
            log.warn("queue:{}没有消费者,消息丢弃", queue);
            return true;
        }
    }


}
